/**
* Copyright (c) 2012-2013 Nokia Corporation. All rights reserved.
* Nokia and Nokia Connecting People are registered trademarks of Nokia Corporation. 
* Oracle and Java are trademarks or registered trademarks of Oracle and/or its
* affiliates. Other product and company names mentioned herein may be trademarks
* or trade names of their respective owners. 
* See LICENSE.TXT for license information.
*/

package com.nokia.example.paint.helpers;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import javax.microedition.lcdui.Image;

/*
 * Class for encoding images to BMP format.
 */
public class BMPEncoder {

    private Image image;
    private final static int BITMAPFILEHEADER_SIZE = 14;
    private final static int BITMAPINFOHEADER_SIZE = 40;
    //Bitmap file header
    private byte bfType[] = {'B', 'M'};
    private int bfSize = 0;
    private int bfReserved1 = 0;
    private int bfReserved2 = 0;
    private int bfOffBits = BITMAPFILEHEADER_SIZE + BITMAPINFOHEADER_SIZE;
    //Bitmap info header
    private int biSize = BITMAPINFOHEADER_SIZE;
    private int biWidth = 0;
    private int biHeight = 0;
    private int biPlanes = 1;
    private int biBitCount = 24;
    private int biCompression = 0;
    private int biSizeImage = 0x030000;
    private int biXPelsPerMeter = 0x0;
    private int biYPelsPerMeter = 0x0;
    private int biClrUsed = 0;
    private int biClrImportant = 0;

    public BMPEncoder(Image image) {
        this.image = image;
        biSizeImage = ((image.getWidth() * image.getHeight()) * 3);  //imagesize
        bfSize = biSizeImage + BITMAPFILEHEADER_SIZE + BITMAPINFOHEADER_SIZE;  //filesize
        biWidth = image.getWidth();
        biHeight = image.getHeight();
    }

    private void createFileHeader(ByteArrayOutputStream bout) throws IOException {
        bout.write(bfType);
        bout.write(intToDWord(bfSize));
        bout.write(intToWord(bfReserved1));
        bout.write(intToWord(bfReserved2));
        bout.write(intToDWord(bfOffBits));
    }

    private void createInfoHeader(ByteArrayOutputStream bout) throws IOException {
        bout.write(intToDWord(biSize));
        bout.write(intToDWord(biWidth));
        bout.write(intToDWord(biHeight));
        bout.write(intToWord(biPlanes));
        bout.write(intToWord(biBitCount));
        bout.write(intToDWord(biCompression));
        bout.write(intToDWord(biSizeImage));
        bout.write(intToDWord(biXPelsPerMeter));
        bout.write(intToDWord(biYPelsPerMeter));
        bout.write(intToDWord(biClrUsed));
        bout.write(intToDWord(biClrImportant));
    }

    public void writeImageToFile(OutputStream filestream) throws IOException {
        ByteArrayOutputStream bout = new ByteArrayOutputStream();
        createFileHeader(bout);
        createInfoHeader(bout);
        int y;
        int i;
        int pad;
        byte rgb[] = new byte[3];
        int[] argb = new int[biWidth * biHeight];
        pad = 4 - ((biWidth * 3) % 4);
        if (pad == 4)  //if 4 we dont need any 
        {
            pad = 0;
        }

        image.getRGB(argb, 0, biWidth, 0, 0, biWidth, biHeight);
        for (y = (biHeight - 1); y >= 0; y--) {
            for (int x = 0; x <= (biWidth - 1); x++) {
                rgb[0] = (byte) (argb[y * biWidth + x] & 0x000000FF);  //blue
                rgb[1] = (byte) ((argb[y * biWidth + x] & 0x0000FF00) >> 8);  //green
                rgb[2] = (byte) ((argb[y * biWidth + x] & 0x00FF0000) >> 16);  //red
                bout.write(rgb);
            }
            for (i = 1; i <= pad; i++) {
                bout.write(0x00);
            }
        }
        filestream.write(bout.toByteArray());
        filestream.flush();
    }

    private byte[] intToWord(int parValue) {
        byte retValue[] = new byte[2];
        retValue[0] = (byte) (parValue & 0x00FF);
        retValue[1] = (byte) ((parValue >> 8) & 0x00FF);
        return (retValue);
    }

    private byte[] intToDWord(int parValue) {
        byte retValue[] = new byte[4];
        retValue[0] = (byte) (parValue & 0x00FF);
        retValue[1] = (byte) ((parValue >> 8) & 0x000000FF);
        retValue[2] = (byte) ((parValue >> 16) & 0x000000FF);
        retValue[3] = (byte) ((parValue >> 24) & 0x000000FF);
        return (retValue);
    }
}
